Нефть – очень важный
стратегический ресурс. Недавно Соединенные Штаты Антарктики вторглись в очень
богатую нефтью страну Кари, и теперь стараются держать контроль над ее
нефтетранспортной системой. Система состоит из трубопроводов, соединяющих
различные узлы – источники нефти и главные города и порты стран. Он
сконструирован таким образом, что можно транспортировать нефть из любого узла в
любой другой.
Однако противостоящие
национальные силы Кари не удовлетворены ситуацией. Они постоянно совершают
теракты, взрывая некоторые нефтепроводы. Недавно террористы решили выполнить
серию взрывов и хотят причинить системе нефтепроводов как можно больший ущерб.
Для каждого трубопровода
террористы знают стоимость взорвать его. Они имеют фиксированную сумму денег и
хотят взорвать как можно больше труб на эту сумму. Однако, поскольку им
по-прежнему нужна нефть для себя в разных регионах страны, они хотят чтобы
система по-прежнему была в состоянии транспортировать нефть из любого узла до
любого другого. Помогите им установить, какие трубы следует взорвать.
Вход.
Первая строка содержит количество вершин n,
количество трубопроводов m и
количество денег s у террористов (2 ≤
n ≤ 50000, 1 ≤ m ≤ 105, 0 ≤ s ≤ 1018). Следующие m строк содержат информацию о трубопроводах
– номера вершин, соединяемые трубопроводом, и стоимость его подрыва (она не
превосходит 109).
Нефть можно передавать вдоль
каждого трубопровода в обоих направлениях, каждые два узла соединены не более
чем одним трубопроводом.
Выход.
Выведите в первой строке максимальное количество трубопроводов, которое смогут
взорвать террористы. Во второй строке выведите номера этих трубопроводов (они
пронумерованы начиная с 1 в порядке их появления во входных данных).
Пример входа |
Пример выхода |
6 7 10 1 2 3 1 3 3 2 3 3 3 4 1 4 5 5 5 6 4 4 6 5 |
2 1 5 |
графы – система
непересекающихся множеств
Найдем
максимальное остовное дерево (остов с максимальным весом). Ребра, не входящие в
него, поместим в массив w. Любое подмножество
ребер из w может
быть удалено. Нам следует максимизировать количество удаленных ребер при
ограниченном количестве
денег s у террористов. Отсортируем
ребра в w по возрастанию веса и будем взрывать
трубопроводы по возрастанию их стоимости до тех пор, пока хватит денег (сумма стоимостей
взорванных трубопроводов не должна превышать s).
Пример
Граф из
примера имеет вид:
Террористы могут взорвать 2
трубопровода: (1, 2) и (4, 5) общей стоимостью 3 +
5 = 8, что не
больше 10.
Этот тест
имеет не единственное решение. Максимальный остов имеет вид:
Массив w будет содержать ребра, не
входящие в максимальный остов: (2, 3), (5, 6). Суммарная стоимость этих ребер
не превосходит 10, поэтому взорвать их можно все.
Реализация алгоритма
Объявим
структуру ребра Edge. Она
содержит соединяющие вершины u и v, вес cost и номер Id ребра.
struct Edge
{
int u, v, cost, Id;
} temp;
Функция Repr возвращает представителя множества, в котором находится n.
int Repr(int n)
{
if (n == mas[n]) return n;
return mas[n] = Repr(mas[n]);
}
Функция Union объединяет множества с элементами x и y.
int Union(int x, int y)
{
int x1 = Repr(x), y1 = Repr(y);
mas[x1] = y1;
return (x1 != y1);
}
Функция – компаратор cmpGreater сортирует
ребра по убыванию веса.
int cmpGreater(Edge a, Edge b)
{
return a.cost > b.cost;
}
Функция – компаратор cmpLess сортирует ребра по возрастанию
веса.
int cmpLess(Edge a, Edge b)
{
return a.cost < b.cost;
}
Основная
часть программы. Читаем входные данные.
scanf("%d %d %lld", &n, &m, &s);
Инициализируем массив mas.
mas.resize(n + 1);
for (i = 1; i <= n; i++) mas[i] = i;
Читаем ребра графа, заносими их в массив e.
for (i = 0; i < m; i++)
{
scanf("%d %d %d", &temp.u,
&temp.v, &temp.cost);
temp.Id = i + 1;
e.push_back(temp);
}
Сортируем ребра по убыванию веса.
sort(e.begin(), e.end(),
cmpGreater);
Строим максимальный остов. Ребра, не вошедшие в него,
заносим в массив w.
for (i = 0; i < e.size(); i++)
{
if (Repr(e[i].u) == Repr(e[i].v))
w.push_back(e[i]);
else
Union(e[i].u, e[i].v);
}
Сортируем ребра в массиве w.
sort(w.begin(), w.end(),
cmpLess);
Удаляем ребра из массива w по возрастанию их стоимости
до тех пор, пока хватит денег. Номера удаляемых ребер заносим в массив EdgeId.
for (i = 0; i < w.size(); i++)
{
if ((s < 0) || (w[i].cost > s)) break;
EdgeId.push_back(w[i].Id);
s -= w[i].cost;
}
Выводим ответ: количество удаленных ребер и их номера.
if (EdgeId.empty()) printf("0\n");
else
{
printf("%d\n", EdgeId.size());
for (i = 0; i < EdgeId.size(); i++)
printf("%d ", EdgeId[i]);
printf("\n");
}